package com.jlect.swebing.ui.client;

import java.util.Iterator;

/**
 * Flow layout manager
 *
 * @author Sergey Kozmin
 * @since 17.11.2007 15:02:53
 */
public class FlowLayout implements LayoutManager {
    public static final int LEFT = 0;
    public static final int CENTER = 1;
    public static final int RIGHT = 2;

    private int align;
    
    private int hgap;
    private int vgap;

    public FlowLayout(int align, int hgap, int vgap) {
        this.hgap = hgap;
        this.vgap = vgap;
        setAlignment(align);
    }

    public FlowLayout(int align) {
        this(align, 0, 0);
    }

    public FlowLayout() {
        this(CENTER);
    }

    public int getAlignment() {
        return align;
    }

    public void setAlignment(int align) {
        this.align = align;
    }

    public int getHgap() {
        return hgap;
    }

    public void setHgap(int hgap) {
        this.hgap = hgap;
    }

    public int getVgap() {
        return vgap;
    }

    public void setVgap(int vgap) {
        this.vgap = vgap;
    }

    public void addLayoutComponent(GComponent component, LayoutConstraint contsraint) {
    }

    public void removeLayoutComponent(GComponent component) {
    }

    public GComponent getLayoutComponent(LayoutConstraint constraint) {
        return null;
    }

    public Dimension preferredLayoutSize(GContainer target) {
        Dimension totalPreffered = new Dimension(0, 0);
        boolean addGap = false;
        for (Iterator iterator = target.getComponentsIterator(); iterator.hasNext();) {
            GComponent component = (GComponent) iterator.next();
            if (component.isVisible()) {
                Dimension dimension = component.getPreferredSize();
                totalPreffered.height = Math.max(totalPreffered.height, dimension.height);
                if (addGap) {
                    totalPreffered.width += hgap;
                } else {
                    addGap = true;
                }
                totalPreffered.width += dimension.width;
            }
        }
        totalPreffered.width += hgap * 2;
        totalPreffered.height += vgap * 2;
        return totalPreffered;
    }

    public Dimension minimumLayoutSize(GContainer target) {
        Dimension totalMinimal = new Dimension(0, 0);
        boolean addGap = false;
        for (Iterator iterator = target.getComponentsIterator(); iterator.hasNext();) {
            GComponent component = (GComponent) iterator.next();
            if (component.isVisible()) {
                Dimension dimension = component.getPreferredSize();
                totalMinimal.height = Math.max(totalMinimal.height, dimension.height);
                if (addGap) {
                    totalMinimal.width += hgap;
                } else {
                    addGap = true;
                }
                totalMinimal.width += dimension.width;
            }
        }
        totalMinimal.width += hgap * 2;
        totalMinimal.height += vgap * 2;
        return totalMinimal;
    }

    public void layoutContainer(GContainer target) {
        FlowPoint prevPoint = new FlowPoint(vgap, 0, 0);
        FlowPoint curPoint = countNextComponents(target, prevPoint.finishY, prevPoint.lastComponentIndex);
        int componentsCount = target.getComponentsCount();
        while(curPoint.lastComponentIndex < componentsCount) {
            setBoundsToPreviousComponents(target, prevPoint.finishY, curPoint.rowLenght, prevPoint.lastComponentIndex, curPoint.lastComponentIndex);
            prevPoint = curPoint;
            curPoint = countNextComponents(target, prevPoint.finishY, prevPoint.lastComponentIndex);//? +1
        }
        setBoundsToPreviousComponents(target, prevPoint.finishY, curPoint.rowLenght, prevPoint.lastComponentIndex, componentsCount);
    }

    private FlowPoint countNextComponents(GContainer target, int startY, int startComponentNumber) {
        int maxX = target.getWidth();
        int maxRowY = 0;
        int currentX = 0;
        for (; startComponentNumber < target.getComponentsCount(); startComponentNumber++) {
            GComponent component = target.getComponentByIndex(startComponentNumber);
            if (component.isVisible()) {
                Dimension dimension = component.getPreferredSize();
                if ((currentX == 0) || ((currentX + dimension.width) <= maxX)) {
                    currentX += dimension.width + hgap;
                    maxRowY = Math.max(maxRowY, dimension.height);
                } else {
                    break;
                }
            }
        }
        return new FlowPoint(startY + maxRowY + vgap, startComponentNumber, currentX);
    }

    private void setBoundsToPreviousComponents(GContainer target, int yLevel, int rowLenght, int startComponentIndex, int finishComponentIndex) {
        boolean leftToRight = target.getOrientation().isLeftToRight();
        int width = target.getWidth();
        int currentX = 0;
        switch (align) {
            case LEFT:
                currentX += leftToRight ? hgap : width - hgap;
                break;
            case CENTER:
                currentX += (width - rowLenght) / 2 + hgap;
                break;
            case RIGHT:
                currentX += leftToRight ? width : hgap;
                break;
        }
        for (int i = startComponentIndex; i < finishComponentIndex; i++) {
            GComponent component = target.getComponentByIndex(i);
            if (component.isVisible()) {
                Dimension preferredSize = component.getPreferredSize();
                if (leftToRight) {
                    component.setBounds(currentX, yLevel, preferredSize.width, preferredSize.height);
                } else {
                    component.setBounds(target.getWidth() - currentX - component.getWidth(), yLevel, preferredSize.width, preferredSize.height);
                }
                currentX += component.getWidth() + hgap;
            }
        }
    }

    private static class FlowPoint {
        int finishY;
        int lastComponentIndex;
        int rowLenght;

        private FlowPoint(int finishY, int lastComponentIndex, int rowLenght) {
            this.finishY = finishY;
            this.lastComponentIndex = lastComponentIndex;
            this.rowLenght = rowLenght;
        }
    }
}
